#include "dkglUtility.h"
#include "dkglVector3.h"

namespace DKGL
{

bool EnableOpenGL(HDC  hdc, HGLRC *hrc)
{
	//PIXELFORMATDESCRIPTOR pfd;
	int format;
	DWORD err_code;
	// set the pixel format for the DC
	
	PIXELFORMATDESCRIPTOR pfd = { 
    sizeof(PIXELFORMATDESCRIPTOR),   // size of this pfd 
    1,							// version number 
    PFD_DRAW_TO_WINDOW |		// support window 
    PFD_SUPPORT_OPENGL |		// support OpenGL 
	PFD_GENERIC_ACCELERATED|	// open OpenGL acceleration
//	PFD_GENERIC_FORMAT|
    PFD_DOUBLEBUFFER,			// double buffered 
    PFD_TYPE_RGBA,				// RGBA type 
    24,                    // 24-bit color depth 
    0, 0, 0, 0, 0, 0,      // color bits ignored 
    0,                     // no alpha buffer 
    0,                     // shift bit ignored 
    0,                     // no accumulation buffer 
    0, 0, 0, 0,            // accum bits ignored 
    16,                    // 16-bit z-buffer 
    0,                     // no stencil buffer 
    0,                     // no auxiliary buffer 
    PFD_MAIN_PLANE,        // main layer 
    0,                     // reserved 
    0, 0, 0                // layer masks ignored 
	}; //pfd
	format = ChoosePixelFormat( hdc, &pfd );
	
	if(SetPixelFormat( hdc, format, &pfd ) == FALSE) {
		err_code = GetLastError();
		throw( "SetPixelFormat Fault!");
		return false;
	}
	// create and enable the render context (RC)
	if((*hrc = wglCreateContext( hdc ))==NULL)
	{
		throw("Create Rendering Context Fault!");
		return false;
	}

	wglMakeCurrent( hdc, *hrc );
	return true;
}

// Disable OpenGL

void DisableOpenGL(HGLRC hRC)
{
	wglMakeCurrent( NULL, NULL );
	wglDeleteContext( hRC );
}

/**
	Real-time Rendering 2nd Edition
*/
void InvAffineMat(float *m)
{
	float tmp[3];

	tmp[0] = -(m[12]*m[0]+m[1]*m[13]+m[2]*m[14]);
	tmp[1] = -(m[12]*m[4]+m[13]*m[5]+m[6]*m[14]);
	tmp[2] = -(m[8]*m[12]+m[9]*m[13]+m[10]*m[14]);
	m[12] = tmp[0];
	m[13] = tmp[1];
	m[14] = tmp[2];
}

void ToCameraMatrix( float *dest, const float *src)
{
	dest[0] = src[0];
	dest[1] = src[4];
	dest[2] = src[8];
	dest[3] = 0.0f;

	dest[4] = src[1];
	dest[5] = src[5];
	dest[6] = src[9];
	dest[7] = 0.0f;

	dest[8] = src[2];
	dest[9] = src[6];
	dest[10] = src[10];
	dest[11] = 0.0f;

	dest[12] = -(src[12]*src[0]+src[1]*src[13]+src[2]*src[14]);
	dest[13] = -(src[12]*src[4]+src[13]*src[5]+src[6]*src[14]);
	dest[14] = -(src[8]*src[12]+src[9]*src[13]+src[10]*src[14]);
	dest[15] = 1.0f;
}

void InvAffineMat(float* inv_dest, const float* inv_src)
{
	CopyMatrix(inv_dest, inv_src);

	inv_dest[12] = -(inv_src[12]*inv_src[0]+inv_src[1]*inv_src[13]+inv_src[2]*inv_src[14]);
	inv_dest[13] = -(inv_src[12]*inv_src[4]+inv_src[13]*inv_src[5]+inv_src[6]*inv_src[14]);
	inv_dest[14] = -(inv_src[8]*inv_src[12]+inv_src[9]*inv_src[13]+inv_src[10]*inv_src[14]);
}

void MultRotPartOnly(float *dest, const float *src)
{
	float tmp[9];

	tmp[0] = dest[0]*src[0] + dest[4]*src[1] + dest[8]*src[2];
	tmp[1] = dest[1]*src[0] + dest[5]*src[1] + dest[9]*src[2];
	tmp[2] = dest[2]*src[0] + dest[6]*src[1] + dest[10]*src[2];

	tmp[3] = dest[0]*src[4] + dest[4]*src[5] + dest[8]*src[6];
	tmp[4] = dest[1]*src[4] + dest[5]*src[5] + dest[9]*src[6];
	tmp[5] = dest[2]*src[4] + dest[6]*src[5] + dest[10]*src[6];

	tmp[6] = dest[0]*src[8] + dest[4]*src[9] + dest[8]*src[10];
	tmp[7] = dest[1]*src[8] + dest[5]*src[9] + dest[9]*src[10];
	tmp[8] = dest[2]*src[8] + dest[6]*src[9] + dest[10]*src[10];

	dest[0] = tmp[0];	dest[1] = tmp[1];	dest[2] = tmp[2];
	dest[4] = tmp[3];	dest[5] = tmp[4];	dest[6] = tmp[5];
	dest[8] = tmp[6];	dest[9] = tmp[7];	dest[10] = tmp[8];

}

void CopyMatrix(float *m_dest, const float *m_source)
{
	int i;
	for(i=0;i<16;i++)
		m_dest[i] = m_source[i];
}

void SetIdentityMatrix(float *m)
{
	int i;
	for(i=0;i<16;i++)
		m[i] = 0.0f;

	m[0] = m[5] = m[10] = m[15] = 1.0f;
}

Vector3 CalculateNormal(const Vector3& p1, const Vector3& p2, const Vector3& p3)
{
	Vector3 v1, v2, ret;

	//Modified by I-Chen 05/22/08
	v1 = p1 - p2;
	v2 = p3 - p2;

	ret = v1.crossProduct(v2);
	ret.normalise();
	return ret;
}

//Calculate the intersection between line and triangle
//The algorithm is surpported from "REAL-TIME RENDERING 2nd" p581
//INPUT:
//	linep,the start points of LINE
// d the direction and |d| = 1
//	tri0,tri1,tri2:the vertice of Triangle
//OUTPUT
//	BOOL beIntersect
//	out u,v,t..........see RTR for defination
//Implemented by doki 2006 4
bool LineAndTriangleIntersection(const Vector3& linep,const Vector3& d, 
								 const Vector3& tri0, const Vector3& tri1, const Vector3& tri2,Vector3& out)
{
	Vector3 e1,e2,p,s,q;
	float a,err,f,u,v,t;

	err = static_cast<float>(pow(10.0f,-5.0f));
	
	e1 = tri1 - tri0;
	e2 = tri2 - tri0;
	p = d.crossProduct(e2);
	//dkCrossProd(&p,d,e2);
	a = e1.dotProduct(p);
	//a = dkDot(e1,p);
	if(a>-err&&a<err)
		return false;

	f = 1.0f/a;
	s = linep - tri0;
	u = s.dotProduct(p)*f;
	if(u<0||u>1.0f)
		return false;
	
	q = s.crossProduct(e1);
	//dkCrossProd(&q,s,e1);

	v = f* d.dotProduct(q);
	//v = f*dkDot(d,q);
	if(v<0||v+u>1.0f)
		return false;

	t = f*e2.dotProduct(q);
	//t = f*dkDot(e2,q);
	out = Vector3(u,v,t);
	return true;
}

using namespace std;

string WChar2Ansi(LPCWSTR pwszSrc)
{
	int nLen = WideCharToMultiByte(CP_ACP, 0, pwszSrc, -1, NULL, 0, NULL, NULL);

	if (nLen<= 0) return std::string("");

	char* pszDst = new char[nLen];
	if (NULL == pszDst) return std::string("");

	WideCharToMultiByte(CP_ACP, 0, pwszSrc, -1, pszDst, nLen, NULL, NULL);
	pszDst[nLen -1] = 0;

	std::string strTemp(pszDst);
	delete [] pszDst;

	return strTemp;
}

wstring Ansi2WChar(LPCSTR pszSrc, int nLen)
{
	int nSize = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pszSrc, nLen, 0, 0);
	if(nSize <= 0) return NULL;

	WCHAR *pwszDst = new WCHAR[nSize+1];
	if( NULL == pwszDst) return NULL;

	MultiByteToWideChar(CP_ACP, 0,(LPCSTR)pszSrc, nLen, pwszDst, nSize);
	pwszDst[nSize] = 0;

	if( pwszDst[0] == 0xFEFF) // skip Oxfeff
	for(int i = 0; i < nSize; i ++)
	pwszDst[i] = pwszDst[i+1];

	wstring wcharString(pwszDst);
	delete pwszDst;

	return wcharString;
}

string ws2s(const std::wstring& inputws)
{ 
	return WChar2Ansi(inputws.c_str());
}

wstring s2ws(const string& s)
{ 
	return Ansi2WChar(s.c_str(), static_cast<int>(s.size()));
}

}
